<?php
/**
 * Http请求客户端类库
 * 支持get、post、head、文件下载
 */

namespace Service\VenderClient\Weixin;;

class Http
{
    public static $HTTP_CODE;
    private static $lastError;
    private static $lastErrorCode;

    /**
     * @throws \Exception
     */
    protected static function is_curl()
    {
		if(extension_loaded('curl')){
			return true;
		}
		throw new \Exception('curl扩展未加载');
	}

    /***
     * 向指定url发送一个post请求
     * @param string $url 待发送请求的url地址
     * @param array|string $data 需要发送的数据
     * @param array $header 附加的header头参数
     * @param float $timeOut 请求超时时间，可以为浮点数，表示毫秒超时
     * @return bool|string 返回false表示失败
     */
	public static function post(string $url, $data, array $header = [], float $timeOut = 5)
    {
		return self::request($url,$data,'post',$header, $timeOut);
	}

    /**
     * 向指定url发送一个get请求
     * @param string $url 待请求url地址
     * @param array $header 附加的header头参数
     * @param float $timeOut 请求超时时间，可以为浮点数，表示毫秒超时
     * @return bool|string 返回false表示失败
     */
    public static function get(string $url,array $header=[], float $timeOut = 5)
    {
        return self::request($url, [], 'get', $header, $timeOut);
    }

    /**
     * 向一个url文件地址发送head请求
     * @param string $url 待请求地址
     * @param array $header 附加的header头参数
     * @param int $timeOut 请求超时时间，可以为浮点数，表示毫秒超时
     * @return array|bool 返回false表示失败
     */
    public static function head(string $url, array $header = [], float $timeOut = 5)
    {
        $result = self::request($url, null, 'head', $header, $timeOut);
        if (is_string($result)) {
            $tmp = explode("\r\n", $result);
            $result = [];
            foreach ($tmp as $item) {
                if (empty($item) || strpos($item, ':') === false) {
                    continue;
                }
                list($key, $val) = explode(':', $item, 2);
                $key = trim($key);
                $val = trim($val);
                $result[$key] = $val;
            }
        }
        return $result;
    }

    /**
     * 下载远程文件到本地
     * @param string $url 待下载文件地址
     * @param string $outPath 下载后的保存路径
     * @param array $header 附加的header头参数
     * @param float $timeout 请求超时时间，可以为浮点数，表示毫秒超时
     * @return bool
     */
    public static function downFile(string $url, string $outPath, array $header = [], float $timeout = 5)
    {
        return self::request($url, [], 'get', $header, $timeout, $outPath);
    }

    /**
     * @param $url
     * @param $data
     * @param string $method
     * @param array $header
     * @param int $timeOut
     * @param bool $downFilePath 不为false时表示下载文件,值为文件保存路径
     * @return bool|mixed
     */
    protected static function request(string $url, $data, string $method='get', array $header=[], float $timeOut = 5, $downFilePath = false)
    {
        self::is_curl();
        if(empty($url)){
        	return false;
        }
        if (empty($header) || !is_array($header)) {
        	$header = [];
        }
        $ch = curl_init();
        curl_setopt($ch,CURLOPT_URL,$url);
        curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
        curl_setopt($ch,CURLOPT_TIMEOUT_MS, $timeOut * 1000);
        curl_setopt($ch,CURLOPT_CONNECTTIMEOUT_MS, $timeOut * 1000);
        curl_setopt($ch,CURLOPT_HTTPHEADER,$header);
        curl_setopt($ch,CURLOPT_FORBID_REUSE,1);
        curl_setopt($ch,CURLOPT_FRESH_CONNECT,1);
        curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
        curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 );
        /* 关闭https验证 */
        if ("https" == substr($url, 0, 5)) {
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        }
        is_array($data) && $data = http_build_query($data);
        self::setDataOpt($method, $ch, $data);
        if (self::is_https($url)) {
            self::https_setopt($ch);
        }
        $fp_output = null;
        if ($downFilePath !== null && is_string($downFilePath)) {
            $fp_output = fopen($downFilePath, 'w');
            curl_setopt($ch, CURLOPT_FILE, $fp_output);
        }
        $result = curl_exec($ch);
        $code = curl_getinfo($ch,CURLINFO_HTTP_CODE);
        self::$lastError = curl_error($ch);
        self::$lastErrorCode = curl_errno($ch);
        curl_close($ch);
        self::$HTTP_CODE = $code;
        if (is_resource($fp_output)) {
            fclose($fp_output);
            if ($result === false) {
                return false;
            }
            return true;
        }
        return $result;
    }

    /**
     * 设置数据选项
     * @param $ch
     * @param string $data
     * @return bool
     */
    protected static function setDataOpt($method, $ch, $data = '')
    {
        $method = strtolower($method);
        if($method == 'post'){
            curl_setopt($ch,CURLOPT_POST,true);
            curl_setopt($ch,CURLOPT_POSTFIELDS,$data);
        } elseif ($method == 'put') {
            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
            curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        } elseif ($method == 'delete') {
            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
        } elseif ($method == 'head') {
            curl_setopt($ch, CURLOPT_NOBODY, true);
            curl_setopt($ch, CURLOPT_HEADER, true);
        }
        return true;
    }

    /**
     * 设置https请求选项
     * @param $ch   curl句柄
     * @return bool
     */
    protected static function https_setopt($ch)
    {
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);    // 不验证https证书
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        return true;
    }

    /**
     * 判断是否https请求
     * @param $url
     * @return bool
     */
    private static function is_https($url)
    {
        $httpProtocol = parse_url($url, PHP_URL_SCHEME);
        if ($httpProtocol == 'https') {
            return true;
        }
        return false;
    }

    /**
     * 获取最后一次请求的Http状态码
     * @return int
     */
    public static function getHttpCode()
    {
        return self::$HTTP_CODE;
    }

    /**
     * 获取本次请求错误信息
     * @return string
     */
    public static function getLastError()
    {
        return self::$lastError;
    }

    /**
     * 获取本次请求的错误编码
     * @return int
     */
    public static function getLastErrno()
    {
        return self::$lastErrorCode;
    }
	
}
